<template>
  <div class="page_root" :key="queryUpdateFlag" data-page-name="平台用户故事" data-page-identification="x_zugnuq856n">
    <a-spin :spinning="pageLoading.loading" :tip="pageLoading.tip">
      <!-- begin vid_7vqxc7rhzg 自定义模板_y7xb4613m4 -->
      <!--            查看问题-->
      <a-modal
        :visible="showMsgState.visible"
        title="用户故事描述"
        :footer="null"
        centered
        @cancel="cancelShowMsg"
        width="1000px"
        :body-style="{
                    maxHeight: '800px',
                    overflowY: 'auto'
                }"
      >
        <div class="item-content" v-html="getHtmlContent(showMsgState.msg)"> </div>
      </a-modal>
      <!--            查看问题-->
      <!--            新建/编辑用户故事-->
      <a-modal
        :visible="milepostState.addOrEditVisible"
        :title="milepostState.addOrEditTitle"
        centered
        :mask-closable="false"
        destroy-on-close
        :body-style="{
                    maxHeight: '800px',
                    overflowY: 'auto'
                }"
        width="1000px"
        @cancel="cancelStory"
      >
        <template #footer>
          <a-button type="primary" @click="cancleMbTask"> 取消</a-button>
          <a-button
            type="primary"
            v-if="
                            milepostState.current == 1 &&
                            milepostState.modalForm.templateTaskId?.length > 0 &&
                            !currentData.id
                        "
            @click="getTask"
            :loading="milepostState.nextLoading"
          >
            下一步</a-button
          >
          <a-button type="primary" v-if="milepostState.current == 2" @click="milepostState.current = 1"> 上一步</a-button>
          <a-button
            type="primary"
            @click="confirmMbTask"
            v-if="
                            milepostState.current == 2 ||
                            (milepostState.modalForm.templateTaskId?.length == 0 && milepostState.current == 1)
                        "
            :loading="milepostState.okLoading"
          >
            确定</a-button
          >
        </template>
        <div class="form-model">
          <a-form :model="milepostState.modalForm" ref="formRef" :rules="rules">
            <a-row v-if="milepostState.current == 1">
              <a-col :span="12">
                <a-form-item label="月份">
                  <a-date-picker
                    v-model:value="milepostState.modalForm.monthlyGoalTargetMonth"
                    value-format="YYYY-MM"
                    picker="month"
                    style="width: 100%"
                    @change="milepostState.modalForm.projectMonthlyGoalManagementId = undefined"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="12">
                <a-form-item label="月度子目标">
                  <a-select
                    :options="milepostState.targetList"
                    v-model:value="milepostState.modalForm.projectMonthlyGoalManagementId"
                    show-search
                    :filterOption="
                                            (input, option) => {
                                                return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                                            }
                                        "
                    allow-clear
                    @focus="getTarget"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="24">
                <a-form-item label="用户故事名称" name="storyName">
                  <a-input v-model:value="milepostState.modalForm.storyName" :placeholder="inputHolder" />
                </a-form-item>
              </a-col>
              <a-col :span="12">
                <a-form-item label="计划起止时间">
                  <a-range-picker style="width: 100%" v-model:value="milepostState.modalForm.timeRangeList" valueFormat="YYYY-MM-DD" />
                </a-form-item>
              </a-col>
              <a-col :span="12">
                <a-form-item label="优先级" name="priority" :rules="[{ required: true, msssage: '请选择优先级', trigger: 'blur' }]">
                  <a-select
                    :options="[
                                            {
                                                label: 'P0',
                                                value: 'P0'
                                            },
                                            {
                                                label: 'P1',
                                                value: 'P1'
                                            },
                                            {
                                                label: 'P2',
                                                value: 'P2'
                                            },
                                            {
                                                label: 'P3',
                                                value: 'P3'
                                            }
                                        ]"
                    v-model:value="milepostState.modalForm.priority"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="24">
                <a-form-item label="描述" name="labels">
                  <QuillEditor v-model:content="milepostState.modalForm.description" ref="quillEditor" :options="editorOption" class="quill-editor" />
                </a-form-item>
              </a-col>
              <a-col :span="24">
                <a-form-item label="标签" name="labels">
                  <a-input v-model:value="milepostState.modalForm.labels" show-count :placeholder="inputHolder" />
                </a-form-item>
              </a-col>
              <a-col :span="24" v-if="!currentData.id">
                <a-form-item
                  label="功能简称"
                  name="functionAbbreviation"
                  :rules="[
                                        {
                                            required: milepostState.modalForm.templateTaskId.length > 0 ? true : false,
                                            message: '请输入功能简称',
                                            trigger: 'blur'
                                        }
                                    ]"
                >
                  <a-input :placeholder="inputHolder" v-model:value="milepostState.modalForm.functionAbbreviation" maxlength="10" show-count />
                </a-form-item>
              </a-col>
              <a-col :span="24" v-if="!currentData.id">
                <a-form-item label="SOP任务">
                  <a-tree-select
                    v-model:value="milepostState.modalForm.templateTaskId"
                    show-search
                    multiple
                    treeCheckable
                    style="width: 100%"
                    placeholder="请选择"
                    allow-clear
                    treeDefaultExpandAll
                    :tree-data="milepostState.treeData"
                    :fieldNames="{
                                            children: 'children',
                                            label: 'taskName',
                                            value: 'taskId',
                                            key: 'taskId'
                                        }"
                    :filterTreeNode="
                                            (inputValue, treeNode) => {
                                                return (
                                                    treeNode.taskName.toLowerCase().indexOf(inputValue.toLowerCase()) >=
                                                    0
                                                )
                                            }
                                        "
                    :max-tag-count="7"
                    @focus="getTreeData"
                  />
                </a-form-item>
              </a-col>
              <a-col :span="24">
                <a-form-item label="备注">
                  <a-textarea v-model:value="milepostState.modalForm.remarks" />
                </a-form-item>
              </a-col>
            </a-row>
            <div v-else>
              <div v-for="(item, index) in milepostState.modalForm.taskList" :key="index" class="form-item">
                <div class="form-content">
                  <a-row class="one-line">
                    <a-col :span="24">
                      <a-form-item
                        label="任务名称"
                        :name="['taskList', index, 'taskName']"
                        :rules="[
                                                    {
                                                        required: true,
                                                        message: '请输入任务名称!'
                                                    },
                                                    {
                                                        validator: () => {
                                                            if (milepostState.modalForm.taskList[index].taskName) {
                                                                if (
                                                                    milepostState.modalForm.taskList[
                                                                        index
                                                                    ].taskName.trim()
                                                                ) {
                                                                    return PromiseProxy.resolve()
                                                                } else {
                                                                    return PromiseProxy.reject('请输入任务名称!')
                                                                }
                                                            } else {
                                                                return PromiseProxy.resolve()
                                                            }
                                                        }
                                                    }
                                                ]"
                      >
                        <a-input disabled :placeholder="inputHolder" v-model:value="milepostState.modalForm.taskList[index].taskName" />
                      </a-form-item>
                    </a-col>
                  </a-row>

                  <a-row>
                    <a-col :span="12">
                      <a-form-item
                        label="预计投入工时"
                        :name="['taskList', index, 'workingHours']"
                        :rules="[
                                                    { required: true, message: '请输入预计投入工时!', trigger: 'blur' },
                                                    {
                                                        pattern: /^(0\.5|[1-9](\d*)?(\.5)?(\.0)?)$/,
                                                        message: '请输入大于0的数字(支持0.5的倍数)',
                                                        trigger: 'blur'
                                                    }
                                                ]"
                      >
                        <a-input placeholder="请输入工时(支持0.5的数字)" v-model:value="milepostState.modalForm.taskList[index].workingHours" :min="0" type="number" suffix="小时" />
                      </a-form-item>
                    </a-col>
                    <a-col :span="12">
                      <a-form-item label="任务起止时间" :name="['taskList', index, 'allTime']" :rules="{ required: true, message: '请选择任务起止时间!' }">
                        <a-range-picker style="width: 100%" v-model:value="milepostState.modalForm.taskList[index].allTime" valueFormat="YYYY-MM-DD" />
                      </a-form-item>
                    </a-col>
                  </a-row>
                  <a-row>
                    <a-col :span="12">
                      <a-form-item label="任务负责人" :name="['taskList', index, 'taskOwner']" :rules="{ required: true, message: '请选择任务负责人!' }">
                        <a-select
                          :placeholder="selectHolder"
                          v-model:value="milepostState.modalForm.taskList[index].taskOwner"
                          showSearch
                          :options="peopleList"
                          :field-names="{ label: 'label', value: 'value' }"
                          :filter-option="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                        />
                      </a-form-item>
                    </a-col>
                    <a-col :span="12">
                      <a-form-item label="审核员" :name="['taskList', index, 'assessorName']" :rules="{ required: true, message: '请选择审核员!' }">
                        <a-select
                          :placeholder="selectHolder"
                          v-model:value="milepostState.modalForm.taskList[index].assessorName"
                          showSearch
                          :options="peopleList"
                          :field-names="{ label: 'label', value: 'value' }"
                          :filter-option="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                        />
                      </a-form-item>
                    </a-col>
                  </a-row>
                  <a-row class="one-line">
                    <a-col :span="24">
                      <a-form-item label="任务执行人" :name="['taskList', index, 'taskParticipantList']" :rules="{ required: true, message: '请选择任务参与人!' }">
                        <a-select
                          :placeholder="selectHolder"
                          v-model:value="
                                                        milepostState.modalForm.taskList[index].taskParticipantList
                                                    "
                          showSearch
                          mode="multiple"
                          :options="peopleList"
                          :field-names="{ label: 'label', value: 'value' }"
                          :filter-option="
                                                        (input, option) => {
                                                            return (
                                                                option.label
                                                                    .toLowerCase()
                                                                    .indexOf(input.toLowerCase()) >= 0
                                                            )
                                                        }
                                                    "
                        />
                      </a-form-item>
                    </a-col>
                  </a-row>
                  <a-row>
                    <a-col v-if="taskPeopleList.length > 0">
                      <div style="width: 700px; margin-left: 115px; margin-bottom: 28px">
                        <a href="javascript:;" v-for="(item, num) in taskPeopleList" @click="handlerAddPtMbPeople(item.value, index)"
                          ><span style="margin: 0 5px">{{ item.value }}</span></a
                        >
                      </div>
                    </a-col>
                  </a-row>
                  <a-row class="one-line">
                    <a-col :span="24">
                      <a-form-item label="备注">
                        <a-textarea v-model:value="milepostState.modalForm.taskList[index].remark" />
                      </a-form-item>
                    </a-col>
                  </a-row>
                </div>
              </div>
            </div>
          </a-form>
        </div>
      </a-modal>
      <!--            新建/编辑用户故事-->
      <div class="body">
        <div class="top-search">
          <a-row :gutter="24">
            <a-col :span="6" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
              <a-form-item label="检索用户故事">
                <a-input v-model:value="milepostState.searchForm.storyName" @change="debounceGetData" :placeholder="inputHolder" />
              </a-form-item>
            </a-col>

            <a-col :span="4" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
              <a-form-item label="状态">
                <a-select
                  :placeholder="selectHolder"
                  allow-clear
                  v-model:value="milepostState.searchForm.storyStatus"
                  :options="milepostState.statusList"
                  @change="getData"
                  :field-names="{
                                        label: 'label',
                                        value: 'label'
                                    }"
                  :showSearch="true"
                  :filterOption="
                                        (input, option) => {
                                            return option.label.toLowerCase().indexOf(input.toLowerCase()) >= 0
                                        }
                                    "
                />
              </a-form-item>
            </a-col>
            <a-col :span="5">
              <a-form-item label="排序" style="margin: 0" :label-col="{ span: 6 }" :wrapper-col="{ span: 18 }">
                <a-select :placeholder="selectHolder" :options="milepostState.sortList" v-model:value="milepostState.searchForm.directionField" @change="getData" allow-clear />
              </a-form-item>
            </a-col>
            <a-col :span="4">
              <a-form-item label="标签" :label-col="{ span: 5 }" :wrapper-col="{ span: 19 }">
                <a-select :placeholder="selectHolder" allow-clear v-model:value="milepostState.searchForm.labels" :options="milepostState.labelList" @change="getData" />
              </a-form-item>
            </a-col>
            <a-col :span="5" style="display: flex; justify-content: end; align-items: start">
              <a-space>
                <a-button @click="resetData" ghost type="primary"> 重置</a-button>
                <a-button v-permission="'50001252_vid_won1imtgwd'" @click="handleAdd" type="primary">新建</a-button>
                <a-button v-permission="'50001252_vid_g9z38wtly0'" v-if="milepostState.showCheck" @click="handlerShowCheck" type="primary" :loading="milepostState.keyNoteLoading">保存</a-button>

                <a-button v-permission="'50001252_vid_ev0ix45257'" v-else @click="handlerShowCheck" type="primary" :loading="milepostState.keyNoteLoading">关键路径</a-button>
              </a-space>
            </a-col>
          </a-row>
        </div>
        <a-spin :spinning="milepostState.dataLoading" tip="加载中">
          <div class="content">
            <div class="no-progress" v-if="milepostState.dataList.length == 0">
              <div class="tip">
                <p class="message">暂无数据</p>
              </div>
            </div>
            <div class="story-item" v-for="(item, index) in milepostState.dataList" :key="item.id">
              <div class="story-title">
                <div class="title-left">
                  <a-checkbox
                    @click.stop=""
                    v-if="milepostState.showCheck"
                    :checked="item.keynote === '是'"
                    @change="
                                            val => {
                                                item.keynote = val.target.checked ? '是' : '否'
                                                modifiedKeynotes.set(item.id, item.keynote) // 记录用户对这个 id 的选择
                                            }
                                        "
                  />
                  <div class="priority-tag" title="优先级" v-if="item.priority" style="background-color: getBgc(item.priority)">{{ item.priority }}</div>
                  <div
                    title="用户故事状态"
                    class="status-tag"
                    :style="{
                                            backgroundColor: getStatusColor(item.storyStatus)
                                        }"
                    >{{ item.storyStatus }}</div
                  >
                  <div v-if="item.keynote === '是'" class="critical-path">关键路径</div>
                  <div class="story-name" :title="item.storyName">{{ item.storyName }}</div>
                </div>
                <div class="title-right">
                  <a-space :size="8">
                    <div class="people-icon"></div>
                    <span class="title">创建人 :</span>
                    <span class="msg">{{ item.createdBy }}</span>

                    <div class="time-icon"></div>
                    <span class="title">创建时间 :</span>
                    <span class="msg">{{ item.createdTime }}</span>
                  </a-space>
                  <a-space :size="4">
                    <a-button size="small" type="link" v-permission="'50001252_vid_zqw6l0h4gd'" @click="showQuestionMsg(item.description)">查看描述</a-button>
                    <a-divider type="vertical" style="margin: 0; background-color: #3875ff" />
                    <a-button size="small" type="link" @click="handleEditStory(item)" v-permission="'50001252_vid_byz2mzv6mu'">编辑</a-button>
                    <a-divider type="vertical" style="margin: 0; background-color: #3875ff" />
                    <a-popconfirm title="确定删除?" ok-text="确定" cancel-text="取消" @confirm="confirmDelete(item.id)" @cancel="cancel" v-permission="'50001252_vid_60a6673vbw'">
                      <a-button v-permission="'50001252_vid_60a6673vbw'" size="small" type="link">删除</a-button>
                    </a-popconfirm>
                  </a-space>
                </div>
              </div>
              <div class="story-content">
                <div class="item-icon" v-if="item.labels">
                  <div v-for="(tag, index) in handlerGetSpace(item.labels)" class="label-item"> <div class="label-img"></div>{{ tag }} </div>
                </div>
                <div class="item-icon target-item">
                  <div class="target-icon"></div>
                  <div class="item-label">所属子目标：</div>
                  <div class="item-content">{{ item.projectMonthlyGoal }}</div>
                </div>
                <a-descriptions title="" bordered :column="5" size="small">
                  <!-- 项目阶段和所属SOP任务各占一半，组成第二行 -->
                  <a-descriptions-item label="计划开始">
                    <div class="item-content" :title="item.startTime">{{ item.startTime }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="实际开始">
                    <div class="item-content" :title="item.actualStartTime">{{
                item.actualStartTime
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="计划完成">
                    <div class="item-content" :title="item.endTime">{{ item.endTime }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="实际完成">
                    <div class="item-content" :title="item.actualEndTime">{{
                item.actualEndTime
                    }}</div>
                  </a-descriptions-item>
                  <!--                                     其余字段每行4个，每个不设置span（即默认1）-->
                  <a-descriptions-item label="SOP任务数">
                    <div class="item-content" :title="item.sopTaskNumber">{{
                item.sopTaskNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="进行中的SOP任务">
                    <div class="item-content" :title="item.runningSopTaskNumber">{{
                item.runningSopTaskNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="子任务数">
                    <div class="item-content" :title="item.otherTaskNumber">{{
                item.otherTaskNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="进行中的子任务">
                    <div class="item-content" :title="item.runningOtherTaskNumber">{{
                item.runningOtherTaskNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="问题任务数">
                    <div class="item-content" :title="item.problemNumber">{{
                item.problemNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="进行中的问题任务" :span="2">
                    <div class="item-content" :title="item.runningProblemNumber">{{
                item.runningProblemNumber
                    }}</div>
                  </a-descriptions-item>
                  <a-descriptions-item label="备注" :span="4">
                    <div class="item-content" :title="item.remarks">{{ item.remarks }}</div>
                  </a-descriptions-item>
                </a-descriptions>
                <!-- 第一行 -->
              </div>
            </div>
          </div>
        </a-spin>
      </div>
      <!-- end vid_7vqxc7rhzg 自定义模板_y7xb4613m4 -->
      <!-- begin vid_yufrmuy1p1 容器_r7c0ybhmb4 -->
      <al-box v-if="false" class="vid_yufrmuy1p1">
        <!-- begin vid_b9yp1prrhr 查看描述 -->
        <a-button v-permission="'3000001021_vid_b9yp1prrhr'" class="vid_b9yp1prrhr"> 查看描述 </a-button>
        <!-- end vid_b9yp1prrhr 查看描述 -->
        <!-- begin vid_apf0s6w3ri 编辑 -->
        <a-button v-permission="'3000001021_vid_apf0s6w3ri'" class="vid_apf0s6w3ri"> 编辑 </a-button>
        <!-- end vid_apf0s6w3ri 编辑 -->
        <!-- begin vid_3w1t5mw3pt 关键路径 -->
        <a-button v-permission="'3000001021_vid_3w1t5mw3pt'" class="vid_3w1t5mw3pt"> 关键路径 </a-button>
        <!-- end vid_3w1t5mw3pt 关键路径 -->
        <!-- begin vid_nvuzhmai4w 新建 -->
        <a-button v-permission="'3000001021_vid_nvuzhmai4w'" class="vid_nvuzhmai4w"> 新建 </a-button>
        <!-- end vid_nvuzhmai4w 新建 -->
        <!-- begin vid_sabdi0uugq 删除 -->
        <a-button v-permission="'3000001021_vid_sabdi0uugq'" class="vid_sabdi0uugq"> 删除 </a-button>
        <!-- end vid_sabdi0uugq 删除 -->
      </al-box>
      <!-- end vid_yufrmuy1p1 容器_r7c0ybhmb4 -->
    </a-spin>
  </div>
</template>
<script setup lang="tsx">
  import {
      h, ref, reactive, toRaw, markRaw, watch, computed,
      onBeforeMount, onMounted, onBeforeUnmount, onUnmounted, onActivated, onDeactivated
    } from "vue";
    import { useRoute, useRouter } from 'vue-router';
    const router = useRouter();
    const route = useRoute();
    import { get, post, put, del, request, requestWithHeaders } from '@/utils/http/Axios';
    import { cloneDeep, localGet, merge, getDifferenceArray, traversalTreeStructure, checkCaseCondition, renderStringInTemplate, handleBreadcrumbJump, throttle, permission, getComponentParamsValue } from '@/utils/index';
    import { isNullOrEmpty, isEmptyArr, isNullOrUnDef, isContained, isArray, isEmpty, isObject } from '@/utils/is';
    import dayjs from 'dayjs';
    import { getPagePcChartsDataSourceConvert } from '@/utils/chart';
    import { message, notification, Modal } from 'ant-design-vue'
    import { propertyFormatByNumber, propertyFormatByPercentage, propertyFormatByDate, parseSubFromDataList } from '@/utils/format';
    import colorTheme from '@/design/custom-theme.json';
    
    import UserTableRecords from '@/model/UserTableRecords';
    import { getAppBaseUrl } from '@/utils/index';
    import { sendToken } from "@/hooks/useIframeToken";
    import { useSystemStore } from "@/store/modules/system";
    const systemStore = useSystemStore();
    



      // 获取传入的流程ID和组件参数
      const props = defineProps({
            'processInstanceId': {
            type: [String,Number,Boolean],
            default: undefined,
        },
      'projectId': {
            type: [String,Number],
            default: undefined,
        },
      })
      const immediateWatch = (source, callback) => {
        watch(source, callback, {deep: true,immediate: true })
      }



  /* -------------------------- 属性定义 -------------------------- */

      const setTimeOutList = [];
      const pageLoading = reactive({
        loading: false,
        tip: undefined
      });
      const authInfo = localGet('app-user-store');




      // 处理校验
      const setSubFormName =(parentInfo, name)=>{
        return [parentInfo.parents, parentInfo.cycleIndex, name];
      }


  /* ========================== 属性定义 ========================== */


  /* -------------------------- 元素数据引用 -------------------------- */


  /* ========================== 元素数据引用 ========================== */


  /* -------------------------- 页面数据 -------------------------- */


  /* ========================== 页面数据 ========================== */



  /* -------------------------- 响应 -------------------------- */

  /* ========================== 响应 ========================== */


  /* -------------------------- 函数 -------------------------- */

  /* ========================== 函数 ========================== */


  /* -------------------------- 页面参数 -------------------------- */

    const pageParameter_state = reactive({
        'processInstanceId': props.processInstanceId ?? route.query.processInstanceId,
    })

  /* ========================== 页面参数 ========================== */


  /* -------------------------- 组件事件处理方法 -------------------------- */

    // ---------------------------- 组件数据同步 ----------------------------

    // ============================ 组件数据同步 ============================


  /* ========================== 组件事件处理方法 ========================== */


  /* -------------------------- 生命周期 -------------------------- */
  // 用于参数变更后重新执行自定义指令
      const queryUpdateFlag = ref(0);
      onMounted(() => { ;

    });

    onUnmounted(() => {
      // 销毁定时器
      setTimeOutList.forEach(({ repeat, timer }) => {
        repeat ?  clearInterval(timer): clearTimeout(timer);
      });
    });

  /* ========================== 生命周期 ========================== */


  /* -------------------------- 数据关联处理 -------------------------- */





  /* -------------------------- 表单数据同步 -------------------------- */
  /* ========================== 表单数据同步 ========================== */

  /* ========================== 数据关联处理 ========================== */


  /* -------------------------- 不同组件自定义的处理逻辑 -------------------------- */

  /* ========================== 不同组件自定义的处理逻辑 ========================== */


  // ================================定制化逻辑========================================
  const { projectId } = props
      const useProjectId = ref()
      import { watchEffect } from 'vue'
      watchEffect(() => {
          if (props.projectId) {
              setTimeout(() => {
                  useProjectId.value = props.projectId
                  getTemplateCode()
                  getPopleList()
                  getAllJfPeople()
                  getData()
                  getStatusList()
                  getLabelList()
                  getParentName()
                  console.log('项目阶段')
              }, 500)
          }
      })
      const inputHolder = '请输入'
      const selectHolder = '请选择'
      const milepostState = reactive({
          searchForm: {
              storyStatus: undefined,
              storyName: undefined,
              directionField: 'created_time-desc',
              labels: undefined
          },
          sortList: [
              {
                  label: '创建时间正序',
                  value: 'created_time-asc'
              },
              {
                  label: '创建时间倒序',
                  value: 'created_time-desc'
              },
              {
                  label: '计划完成时间正序',
                  value: 'end_time-asc'
              },
              {
                  label: '计划完成时间倒序',
                  value: 'end_time-desc'
              },
              {
                  label: '计划开始时间正序',
                  value: 'start_time-asc'
              },
              {
                  label: '计划开始时间倒序',
                  value: 'start_time-desc'
              },
              {
                  label: '优先级正序',
                  value: 'priority-asc'
              },
              {
                  label: '优先级倒序',
                  value: 'priority-desc'
              }
          ],
          dataList: [],
          treeData: [],
          targetKeys: [],
          selectKeys: [],
          SopTaskList: [],
          addOrEditVisible: false,
          dataLoading: false,
          nextLoading: false,
          okLoading: false,
          addOrEditTitle: '',
          modalForm: {
              storyName: undefined,
              priority: undefined,
              labels: undefined,
              timeRangeList: [],
              taskList: [],
              templateTaskId: [],
              monthlyGoalTargetMonth: undefined,
              projectMonthlyGoalManagementId: undefined,
              remarks: undefined
          },
          current: 1,
          statusList: [],
          labelList: [],
          showCheck: false,
          keyNoteLoading: false,
          targetList: [],
          parentName: undefined
      })
      const validatePriority = (rule, value) => {
          if (value === undefined || value === null || value === '') {
              return Promise.reject(new Error('请输入优先级'))
          }
          const number = Number(value)
          const isValid = number > 0 && number < 100 && /^(\d{1,2})(\.\d)?$/.test(value.toString())
          return isValid ? Promise.resolve() : Promise.reject(new Error('请输入 1 到 99 之间，最多一位小数的正数'))
      }
      const formRef = ref(null)
      const rules = {
          storyName: [
              {
                  required: true,
                  message: '请输入用户故事名称',
                  trigger: 'blur'
              }
          ],
          allTime: [{ required: false, message: '请选择任务起止时间!', trigger: 'blur' }]
          // functionAbbreviation: [{ required: true, message: '请输入功能简称', trigger: 'blur' }]
      }
      const currentData = ref({})
      // 新建弹窗
      function handleAdd() {
          milepostState.addOrEditVisible = true

          milepostState.addOrEditTitle = '新建用户故事'
          currentData.value = {}
      }
      // 获取目标
      // 根据id查目录
      function getParentName() {
          get(`/solution-ppm/api/v2/built-in-system-ppm/pmgf/menu_name/${useProjectId.value}`).then(data => {
              milepostState.parentName = data.data
          })
      }
      // 获取列表
      function getData() {
          if (!useProjectId.value) {
              message.info('请选择项目')
              return
          }
          milepostState.dataLoading = true
          post('/solution-ppm/api/v2/built-in-system-ppm/project/story/search', {
              projectId: useProjectId.value,
              ...milepostState.searchForm
          })
              .then(data => {
                  if (data.code !== 200) {
                      message.error(data.message)
                      milepostState.dataList = []
                      return
                  }
                  milepostState.dataList = data.data
                  initKeynotes(milepostState.dataList)
              })
              .finally(() => {
                  milepostState.dataLoading = false
              })
      }

      function getTarget() {
          post('/solution-ppm/api/v2/built-in-system-ppm/pmgf/get/monthlyGoals', {
              targetMonthList: [
                  milepostState.modalForm.monthlyGoalTargetMonth,
                  milepostState.modalForm.monthlyGoalTargetMonth
              ],
              projectNameList: [milepostState.parentName]
          }).then(data => {
              if (data.code !== 200) {
                  message.error(data.message)
                  return
              }
              const allSubGoals = []

              data.data.monthlyGoalManagementDtoList?.forEach(item => {
                  item.monthlyGoalTaskDtoList?.forEach(sub => {
                      allSubGoals.push({
                          label: sub.projectMonthlyGoal,
                          value: sub.id
                      })
                  })
              })
              milepostState.targetList = allSubGoals
          })
      }
      // 重置
      function resetData() {
          milepostState.searchForm.storyStatus = milepostState.searchForm.storyName = undefined
          milepostState.searchForm.directionField = 'created_time-desc'
          milepostState.searchForm.labels = undefined
          getData()
      }
      //     新建下一步获取任务
      function getTask() {
          formRef.value.validate().then(() => {
              createSopDeltaDate.value = quillEditor.value.getContents()
              milepostState.nextLoading = true
              post('/solution-ppm/api/v2/built-in-system-ppm/v2/platform/generate_task', {
                  templateTaskId: milepostState.modalForm.templateTaskId,
                  storyId: milepostState.modalForm.storyId,
                  functionAbbreviation: milepostState.modalForm.functionAbbreviation,
                  projectId: useProjectId.value
              })
                  .then(data => {
                      milepostState.current = 2
                      milepostState.modalForm.taskList = data.data.map(item => {
                          return {
                              ...item,
                              allTime: [item.startTime, item.endTime]
                          }
                      })
                  })
                  .finally(() => {
                      milepostState.nextLoading = false
                  })
          })
      }
      const taskPeopleList = ref([])
      function getPopleList() {
          post(`/solution-ppm/api/v2/built-in-system-ppm/task/getTopUser/${useProjectId.value}`).then(data => {
              taskPeopleList.value = data.data.map(item => {
                  return {
                      label: item,
                      value: item
                  }
              })
          })
      }
      const peopleList = ref([])
      function getAllJfPeople() {
          get('/solution-ppm/api/v2/built-in-system-ppm/pmgf/get/allUser').then(({ data }) => {
              peopleList.value = data.userDtoList.map(item => {
                  return {
                      label: item.name,
                      value: item.name
                  }
              })
          })
      }
      // 快捷添加执行人
      function handlerAddPtMbPeople(name, index) {
          if (milepostState.modalForm.taskList[index].taskParticipantList.indexOf(name) !== -1) {
              milepostState.modalForm.taskList[index].taskParticipantList = milepostState.modalForm.taskList[
                  index
              ].taskParticipantList.filter(item => item !== name)
          } else {
              milepostState.modalForm.taskList[index].taskParticipantList.push(name)
          }
      }
      function cancelStory() {
          currentData.value = {}
          milepostState.addOrEditVisible = false
          milepostState.modalForm = {
              storyName: undefined,
              priority: undefined,
              labels: undefined,
              timeRangeList: [],
              taskList: [],
              templateTaskId: [],
              monthlyGoalTargetMonth: undefined,
              projectMonthlyGoalManagementId: undefined,
              remarks: undefined
          }
          milepostState.current = 1
      }
      const colorMap = {
          '进行中': '#3875FF',
          '完成': '#52C41A',
          '未启动': '#8C8C8C',
          '未开始': '#8C8C8C',
          '已完成': '#52C41A'
      }
      function getStatusColor(status) {
          return colorMap[status] || '#8C8C8C'
      }
      const projectTemplate = ref()
      function getTemplateCode() {
          get(`/solution-ppm/api/v2/built-in-system-ppm/project/${useProjectId.value}`).then(data => {
              projectTemplate.value = data.projectTemplate
          })
      }

      //     获取穿梭框
      function getTreeData() {
          get(
              `/solution-ppm/api/v2/built-in-system-ppm/v2/platform/template_milepost_task?templateCode=${projectTemplate.value}&functionAbbreviation=${milepostState.modalForm.functionAbbreviation}&projectId=${useProjectId.value}`
          ).then(data => {
              milepostState.treeData = data.data
              // flatten(JSON.parse(JSON.stringify(milepostState.treeData)))
              console.log(milepostState.treeData)
              milepostState.modalForm.selectKeys = []
          })
      }
      const quillEditor = ref(null)
      const createSopDeltaDate = ref()
      // 保存模板项目
      function confirmMbTask() {
          formRef.value.validate().then(async () => {
              milepostState.okLoading = true
              milepostState.modalForm.taskList.forEach(item => {
                  item.startTime = item.allTime[0]
                  item.endTime = item.allTime[1]
              })
              let deltaContent = undefined
              if (currentData.value.id) {
                  deltaContent = quillEditor.value.getContents()
              } else {
                  if (milepostState.current === 1) {
                      deltaContent = quillEditor.value.getContents()
                  } else {
                      deltaContent = createSopDeltaDate.value
                  }
              }
              // const deltaContent = currentData.value.id ? quillEditor.value.getContents() : createSopDeltaDate.value

              const imageUploadPromises = []
              deltaContent.ops.forEach(op => {
                  if (op.insert && op.insert.image && op.insert.image.startsWith('data:')) {
                      const localSrc = op.insert.image

                      // 如果是 base64 图片，上传并替换为 URL
                      const file = dataURLtoFile(localSrc, 'image.png') // 将 base64 转换为 File 对象

                      const uploadPromise = uploadFileAPI(file).then(({ data: { fullUrl } }) => {
                          // 替换 op 中的 base64 URL 为服务器返回的 URL
                          op.insert.image = fullUrl
                      })

                      imageUploadPromises.push(uploadPromise)
                  }
              })

              const api = currentData.value.id ? put : post
              try {
                  await Promise.all(imageUploadPromises)
                  api(`/solution-ppm/api/v2/built-in-system-ppm/project/story`, {
                      projectId: useProjectId.value,
                      timeRangeList: milepostState.modalForm.timeRangeList,
                      storyName: milepostState.modalForm.storyName,
                      ppmPlatformTaskFormList: milepostState.modalForm.taskList,
                      id: currentData.value.id,
                      priority: milepostState.modalForm.priority,
                      labels: milepostState.modalForm.labels,
                      description: JSON.stringify(deltaContent),
                      monthlyGoalTargetMonth: milepostState.modalForm.monthlyGoalTargetMonth,
                      projectMonthlyGoalManagementId: milepostState.modalForm.projectMonthlyGoalManagementId,
                      remarks: milepostState.modalForm.remarks
                  })
                      .then(data => {
                          if (data.code !== 200) {
                              message.error(data.message)
                              return
                          }
                          message.success(data.message)

                          cancleMbTask()

                          getData()
                      })
                      .finally(() => {
                          milepostState.okLoading = false
                      })
              } catch (error) {
                  milepostState.okLoading = false
                  console.error('图片上传失败', error)
                  return // 终止提交
              }
          })
      }
      // 关闭模板任务新建
      function cancleMbTask() {
          milepostState.addOrEditVisible = false
          milepostState.current = 1
          milepostState.modalForm.functionAbbreviation = undefined
          milepostState.modalForm.templateTaskId = undefined
          milepostState.modalForm = {
              templateTaskId: [],
              projectId: useProjectId.value,
              functionAbbreviation: undefined,
              taskList: []
          }
      }
      const PromiseProxy = Promise
      function confirmDelete(id) {
          del(`/solution-ppm/api/v2/built-in-system-ppm/project/story/${id}`).then(data => {
              if (data.code !== 200) {
                  message.error(data.message)
                  return
              }
              message.success(data.message)
              getData()
          })
      }
      function useDebounce(fn: (...args: any[]) => void, delay = 500) {
          let timer: ReturnType<typeof setTimeout> | null = null

          return function (...args: any[]) {
              if (timer) clearTimeout(timer)
              timer = setTimeout(() => {
                  fn(...args)
              }, delay)
          }
      }
      // 包一层防抖函数
      const debounceGetData = useDebounce(getData, 500)
      //     编辑story
      async function handleEditStory(record) {
          const item = cloneDeep(record)
          Object.assign(milepostState.modalForm, item)
          milepostState.modalForm.timeRangeList = !item.startTime || !item.endTime ? [] : [item.startTime, item.endTime]
          currentData.value = item
          milepostState.addOrEditVisible = true
          milepostState.addOrEditTitle = '编辑用户故事'
          getTarget()
          nextTick(() => {
              let descriptionData = JSON.parse(record.description)

              quillEditor?.value?.setContents(descriptionData)
          })
      }
      function getStatusList() {
          get('/solution-ppm/api/v2/built-in-system-ppm/dictionary/search/PTYHGS.RWZT').then(data => {
              if (data.code !== 200) {
                  message.error(data.message)
                  return
              }
              milepostState.statusList = data.data.map(item => {
                  return {
                      label: item.itemName,
                      value: item.itemCode
                  }
              })
          })
      }
      function handlerGetSpace(data) {
          return data?.split(/[,;；]/)
      }
      function getLabelList() {
          get(`/solution-ppm/api/v2/built-in-system-ppm/v3/platform_task/story_labels/${useProjectId.value}`).then(
              data => {
                  milepostState.labelList = data.data
              }
          )
      }
      //     增加富文本
      //     富文本设置
      import { nextTick } from 'vue'
      import { QuillEditor, Quill } from '@vueup/vue-quill'
      import ImageUploader from 'quill-image-uploader'
      import '@vueup/vue-quill/dist/vue-quill.snow.css'
      import { take } from 'lodash-es'
      const titleConfig = [
          { Choice: ' .ql-bold', title: '加粗' },
          { Choice: ' .ql-italic', title: '斜体' },
          { Choice: ' .ql-underline', title: '下划线' },
          { Choice: ' .ql-strike', title: '删除线' },
          { Choice: ' .ql-blockquote', title: '块引用' },
          { Choice: ' .ql-code-block', title: '插入代码' },
          { Choice: ' .ql-size', title: '字体大小' },
          { Choice: ' .ql-size', title: '字体大小' },
          { Choice: ' .ql-color', title: '字体颜色' },
          { Choice: ' .ql-background', title: '字体背景颜色' },
          { Choice: ' .ql-list[value="ordered"]', title: '有序列表' },
          { Choice: ' .ql-list[value="bullet"]', title: '无序列表' },
          { Choice: ' .ql-header[value="1"]', title: '1级标题' },
          { Choice: ' .ql-header[value="2"]', title: '2级标题' },
          { Choice: ' .ql-align', title: '对齐方式' },
          { Choice: ' .ql-clean', title: '清除字体格式' },
          { Choice: ' .ql-image', title: '图像' },
          { Choice: ' .ql-size .ql-picker-item[data-value="small"]', title: '小号' },
          { Choice: ' .ql-size .ql-picker-item[data-value="large"]', title: '大号' },
          { Choice: ' .ql-size .ql-picker-item[data-value="huge"]', title: '超大号' },
          { Choice: ' .ql-size .ql-picker-item:nth-child(2)', title: '标准' },
          { Choice: ' .ql-align .ql-picker-item:first-child', title: '居左对齐' },
          { Choice: ' .ql-align .ql-picker-item[data-value="center"]', title: '居中对齐' },
          { Choice: ' .ql-align .ql-picker-item[data-value="right"]', title: '居右对齐' },
          { Choice: ' .ql-align .ql-picker-item[data-value="justify"]', title: '两端对齐' }
      ]
      // 注册图片上传模块
      Quill.register('modules/imageUploader', ImageUploader)

      // 将本地文件转换为URL
      const getLocalImageURL = file => {
          return new Promise(resolve => {
              const reader = new FileReader()
              reader.onload = e => {
                  resolve(e.target.result)
              }
              reader.readAsDataURL(file)
          })
      }

      // 表单数据

      // Quill 编辑器选项
      const editorOption = ref({
          modules: {
              // imageUploader: {
              //     upload: async file => {
              //         const localUrl = await getLocalImageURL(file)
              //         return localUrl // 返回本地图片的 URL 进行预览
              //     }
              // },
              toolbar: [
                  ['bold', 'italic', 'underline', 'strike'], // 加粗，斜体，下划线，删除线
                  ['blockquote', 'code-block'], // 引用，代码块
                  [{ header: 1 }, { header: 2 }], // 标题1，标题2
                  [{ list: 'ordered' }, { list: 'bullet' }], // 有序列表，无序列表
                  [{ size: ['small', false, 'large', 'huge'] }], // 文字大小
                  [{ color: [] }, { background: [] }], // 字体颜色，背景颜色
                  [{ align: [] }], // 对齐
                  ['clean'], // 清除格式
                  ['image'] // 链接，图片，视频
              ]
              // ImageUploader: imageHandler
          }
      })
      // 上传API
      const uploadFileAPI = async file => {
          const data = new FormData()
          data.append('file', file)

          return post('/file-storage/api/v1/file/upload', data)
              .then(({ data: { fullUrl } }) => {
                  return { data: { fullUrl } }
              })
              .catch(error => {
                  console.error('上传图片出错:', error)
                  throw error // 抛出错误以供外部处理
              })
      }

      // 将 dataURL 转换为 File 对象的辅助函数
      const dataURLtoFile = (dataurl, filename) => {
          const arr = dataurl.split(',')
          const mime = arr[0].match(/:(.*?);/)[1]
          const bstr = atob(arr[1])
          let n = bstr.length
          const u8arr = new Uint8Array(n)
          while (n--) {
              u8arr[n] = bstr.charCodeAt(n)
          }
          return new File([u8arr], filename, { type: mime })
      }

      function initTitle() {
          document.getElementsByClassName('ql-editor')[0].dataset.placeholder = ''
          for (let item of titleConfig) {
              let tip = document.querySelector('.ql-toolbar' + item.Choice)
              if (!tip) continue
              tip.setAttribute('title', item.title)
          }
      }
      const showMsgState = reactive({
          visible: false,
          msg: undefined,
          title: ''
      })
      function showQuestionMsg(msg) {
          if (!msg) {
              message.info('暂无描述')
              return
          }
          showMsgState.msg = msg
          showMsgState.visible = true
      }
      function cancelShowMsg() {
          showMsgState.msg = undefined
          showMsgState.visible = false
      }
      //   格式化文本展示
      import { QuillDeltaToHtmlConverter } from 'quill-delta-to-html'
      function getHtmlContent(input) {
          if (!input) return ''
          let deltaObj = null
          try {
              const parsed = typeof input === 'string' ? JSON.parse(input) : input
              if (parsed?.ops && Array.isArray(parsed.ops)) {
                  deltaObj = parsed
              }
          } catch (e) {}

          if (deltaObj) {
              try {
                  const converter = new QuillDeltaToHtmlConverter(deltaObj.ops, {
                      inlineStyles: false
                  })
                  let html = converter.convert()

                  // 预览支持：用原生 <img> 包裹在一个带点击预览功能的 <a> 中
                  html = html.replace(/<img[^>]*src="([^"]+)"[^>]*>/g, (match, src) => {
                      return `<img src="${src}" class="previewable-image" title="点击查看图片"/>`
                  })

                  return html
              } catch (err) {
                  console.error('富文本转HTML失败', err)
                  return ''
              }
          }

          return String(input).replace(/\n/g, '<br/>')
      }
      //     关键路径改
      // function handlerShowCheck() {
      //     console.log('触发')
      //     if (!milepostState.showCheck) {
      //         milepostState.showCheck = true
      //     } else {
      //         let checkedList = milepostState.dataList.filter(item => item.keynote === '是').map(item => item.id)
      //         milepostState.keyNoteLoading = true
      //         // 提交
      //         put(
      //             `/solution-ppm/api/v2/built-in-system-ppm/project/story/keynote_story/${useProjectId.value}`,
      //             checkedList
      //         )
      //             .then(data => {
      //                 if (data.code !== 200) {
      //                     message.error(data.message)
      //                     return
      //                 }
      //                 message.success(data.message)
      //                 milepostState.showCheck = false
      //                 getData()
      //             })
      //             .finally(() => {
      //                 milepostState.keyNoteLoading = false
      //             })
      //     }
      // }
      const modifiedKeynotes = reactive(new Map()) // key: id, value: '是' or '否'
      const initialKeynotes = new Map()
      function handlerShowCheck() {
          console.log('触发')
          if (!milepostState.showCheck) {
              milepostState.showCheck = true
          } else {
              const checkedList = Array.from(modifiedKeynotes.entries())
                  .filter(([_, val]) => val === '是')
                  .map(([id]) => id)

              milepostState.keyNoteLoading = true
              const added = []
              const removed = []
              modifiedKeynotes.forEach((val, id) => {
                  const original = initialKeynotes.get(id)
                  if (original !== val) {
                      if (val === '是') added.push(id)
                      else removed.push(id)
                  }
              })
              put(`/solution-ppm/api/v2/built-in-system-ppm/project/story/keynote_story/${useProjectId.value}`, {
                  addedKeynotes: added,
                  removedKeynotes: removed
              })
                  .then(data => {
                      if (data.code !== 200) {
                          message.error(data.message)
                          return
                      }
                      message.success(data.message)
                      milepostState.showCheck = false
                      modifiedKeynotes.clear() // 提交后清除记录
                      getData()
                  })
                  .finally(() => {
                      milepostState.keyNoteLoading = false
                  })
          }
      }
      function getBgc(type) {
          if (type === 'P0') {
              return '#FF4D4F'
          } else if (type === 'P1') {
              return '#FF7A45'
          } else if (type === 'P2') {
              return '#FFA940'
          } else if (type === 'P3') {
              return '#FFC53D'
          }
      }
      onMounted(() => {
          document.addEventListener('click', e => {
              const target = e.target as HTMLElement
              if (target.tagName === 'IMG' && target.classList.contains('previewable-image')) {
                  const imgUrl = target.getAttribute('src')
                  if (imgUrl) {
                      // 预览逻辑，如弹窗显示 imgUrl
                      previewImage(imgUrl)
                  }
              }
          })
      })
      function previewImage(url) {
          const imgWindow = window.open()
          if (imgWindow) {
              imgWindow.document.write(`<img src="${url}" style="max-width:100%"/>`)
          }
      }
      function initKeynotes(list) {
          initialKeynotes.clear() // 清空旧的
          list.forEach(item => {
              initialKeynotes.set(item.taskId, item.keynote)
          })
      }
</script>
<style lang="less" scoped>
  .page_root {
  width: 100%;
  height: 100%;
  overflow: auto;
  }
  .page_root :deep(.@{ant-prefix}-spin-nested-loading),
  .page_root :deep(.@{ant-prefix}-spin-container)
   {
      height: 100%;
  }
  .page_root :deep(.@{ant-prefix}-spin-container .@{ant-prefix}-table-wrapper)
   {
      display: flex;
      flex-flow: column nowrap;
      justify-content: flex-start;
  }
  .page_root :deep(.@{ant-prefix}-table)
   {
    flex: auto;
    overflow: auto;
  }
  .table-moreBtn_down {
    overflow: hidden; white-space: nowrap; text-overflow: ellipsis; width: 100px;
  }

  .table-moreBtn_menu :deep(.table-moreBtn_menu-item):hover {
    background: #ebf1ff;
  }

  /*  表格树结构样式处理，展开按钮层级增加*/
  :deep(.@{ant-prefix}-table-row-expand-icon) {
    z-index: 1;
  }
  /*  表格树结构 由于每个表格项采用自定义组件，使用绝对定位布局，开启树结构导致显示异常，暂时使用层级定位位置 */
  :deep(.@{ant-prefix}-table-row-level-1 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
    left: 15px !important;
    width: calc(100% - 15px);
  }
  :deep(.@{ant-prefix}-table-row-level-2 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
    left: 30px !important;
    width: calc(100% - 30px);
  }
  :deep(.@{ant-prefix}-table-row-level-3 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
    left: 45px !important;
    width: calc(100% - 45px);
  }
  :deep(.@{ant-prefix}-table-row-level-4 .@{ant-prefix}-table-cell-with-append .cell-wrapper-left) {
    left: 60px !important;
    width: calc(100% - 60px);
  }

  /* 穿梭框改变默认样式，为根据组件样式控制左右栏宽度。 */
  :deep(.@{ant-prefix}-transfer-list)
   {
      height: 100%;
      width: 50%;
  }
  .vid_yufrmuy1p1{
      padding-top:24px;
      padding-bottom:24px;
      background-color:#F80C0C
  }
  .vid_b9yp1prrhr{
      box-shadow:0px 0px 0px 0px
  }
  .vid_3w1t5mw3pt{
      box-shadow:0px 0px 0px 0px
  }

  .content {
          position: relative;
          height: calc(100vh - 300px);
          overflow-y: auto;
      }
      .no-progress {
          height: 370px;
          width: 100%;
          background-color: #fff;
          display: flex;
          justify-content: center;
          border: 1px solid #fff;
          border-radius: 10px;
          position: relative;
          /* 水平居中 */
      }
      .no-progress .tip {
          width: 200px;
          height: 101px;
          background-image: url('@/assets/images/903bc3976708306bda3d72329b485333.png');
          background-size: cover;
          position: absolute;
          top: 30%;
          color: rgb(89, 89, 89);
      }
      .no-progress .tip .message {
          position: absolute;
          left: 35%;
          bottom: -40px;
          font-size: 16px;
          color: rgb(89, 89, 89);
      }
      .form-model :deep(.ant3-form-item-label) {
          width: 110px;
      }
      .story-item {
          background: #ffffff;
          box-shadow: 0 2px 5px 0 rgba(0, 0, 0, 0.2);
          border-radius: 4px;
          margin: 1px 1px 24px 1px;
      }
      .story-title {
          display: flex;
          justify-content: space-between;
          background: rgba(56, 117, 255, 0.1);
          border-radius: 4px 4px 0 0;
          padding: 17px 24px;
          gap: 16px;
      }
      .title-left {
          display: flex;
          gap: 8px;
          font-size: 16px;
          color: #262626;
          letter-spacing: 0;
          font-weight: 600;
          align-items: center;
          flex: 1;
          min-width: 0; /* 防止子元素撑破父容器 */
      }
      .status-tag,
      .priority-tag {
          font-size: 14px;
          color: #ffffff;
          letter-spacing: 0;
          text-align: center;
          line-height: 22px;
          font-weight: 500;
          width: 55px;
          height: 22px;
          border-radius: 2px;
          text-align: center;
      }
      .priority-tag {
          background: #40a9ff;
          border-radius: 2px;
      }
      .story-content {
          padding: 16px 24px;
      }
      .one-line,
      .two-line {
          display: flex;
          gap: 24px;
          align-items: center;
      }
      .one-line {
          margin-bottom: 12px;
      }
      .row-item {
          flex: 1 1 calc((100% - 5 * 24px) / 6); /* 每行 6 项，5 个间距 */
          display: flex;
          flex-direction: row;
          min-width: 0;
      }
      .one-line .row-item:first-child,
      .two-line .row-item:first-child {
          flex: 0 0 auto; /* 内容自适应宽度，不参与均分 */
          max-width: 160px; /* 可选：防止特别长时撑开，视情况而定 */
      }

      .one-line .row-item:not(:first-child),
      .two-line .row-item:not(:first-child) {
          flex: 1; /* 剩下的元素平均分配 */
      }
      .item-title {
          white-space: nowrap;
          font-family: PingFangSC-Regular;
          font-size: 14px;
          color: #595959;
          letter-spacing: 0;
          text-align: right;
          font-weight: 400;
      }

      .item-content {
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          cursor: default;
          font-family: PingFangSC-Medium;
          font-size: 14px;
          color: #262626;
          letter-spacing: 0;
          font-weight: 600;
      }
      .item-icon {
          display: flex;
          flex-wrap: wrap;
          gap: 8px; /* 可选：让 tag 之间有间距 */
          margin-bottom: 16px;
      }
      .label-item {
          display: flex;
          background-color: #eeeeee;
          align-items: center;
          padding: 1px 8px;
          font-size: 14px;
          color: #595959;
          letter-spacing: 0;
          text-align: center;
          font-weight: 400;
          gap: 4px;
          border-radius: 2px;
      }
      .label-img {
          background-image: url('@/assets/images/41210ac3a80806ed1005d4eea85d458a.png');
          background-size: cover;
          width: 10px;
          height: 10px;
      }
      :deep(.ql-toolbar) {
          background: #fafafa;
          border: 1px solid #d9d9d9;
          border-radius: 2px 2px 0 0;
      }
      :deep(.ql-blank) {
          min-height: 110px;
          background: #ffffff;
      }
      :deep(.ql-editor) {
          min-height: 110px;
          background: #ffffff;
      }
      .btn-opration {
          background-color: #ebf1ff;
          color: #3875ff;
      }
      :deep(.ant3-descriptions-view) {
          table {
              table-layout: fixed !important; /* 固定表格布局 */
              width: 100% !important;
              color: red;
          }
      }
      :deep(.ant3-descriptions-view) {
          table {
              table-layout: fixed !important; /* 固定表格布局 */
              width: 100% !important;
              color: red;
          }
      }
      :deep(.ant3-descriptions-bordered.ant3-descriptions-small .ant3-descriptions-item-label) {
          padding: 8px 8px;
      }
      :deep(.ant3-descriptions-bordered.ant3-descriptions-small .ant3-descriptions-item-content) {
          padding: 8px 8px;
      }
      .time-icon,
      .people-icon,
      .jz-icon {
          width: 16px;
          height: 16px;
          background-size: cover;
      }
      .time-icon {
          background-image: url('@/assets/images/39d3679c035fcca597f241d2a993ca2d.png');
      }
      .people-icon {
          background-image: url('@/assets/images/11cd4c52425c55e07df4079d77072f8c.png');
      }
      .story-name {
          white-space: nowrap;
          overflow: hidden;
          text-overflow: ellipsis;
          flex: 1;
          min-width: 0; /* 必须设置 */
      }
      .title {
          font-size: 14px;
          color: #595959;
          letter-spacing: 0;
          font-weight: 400;
      }
      .msg {
          font-size: 14px;
          color: #262626;
          letter-spacing: 0;
          text-align: right;
          font-weight: 500;
      }
      .reset-btn {
          color: #262626;
          background-color: #eeeeee;
          border-color: #eeeeee;
          border-radius: 4px;
      }
      .reset-btn:hover {
          background: #3875ff;
          border-color: #d9d9d9;
          color: #fff;
      }
      .target-item {
          align-items: center;
      }
      .target-icon {
          width: 16px;
          height: 16px;
          background-image: url('@/assets/images/5457dd41e8ffbba86c21cec0f78e4349.png');
          background-size: cover;
      }
      .title-right {
          display: flex;
          align-items: center;
          gap: 16px;
      }
      .critical-path {
          font-size: 14px;
          color: #ffffff;
          letter-spacing: 0;
          text-align: center;
          line-height: 22px;
          font-weight: 500;
          height: 22px;
          border-radius: 2px;
          text-align: center;
          padding: 0 8px;
          border-radius: 2px;
          min-width: 72px;
          background: #36cfc9;
      }
</style>